home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / comm / mail / Mutt089src.lha / Mutt-0.89i-AMIGA / src / imap.c < prev    next >
C/C++ Source or Header  |  1998-01-28  |  20KB  |  919 lines

  1. /*
  2.  * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
  3.  * 
  4.  *     This program is free software; you can redistribute it and/or modify
  5.  *     it under the terms of the GNU General Public License as published by
  6.  *     the Free Software Foundation; either version 2 of the License, or
  7.  *     (at your option) any later version.
  8.  * 
  9.  *     This program is distributed in the hope that it will be useful,
  10.  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *     GNU General Public License for more details.
  13.  * 
  14.  *     You should have received a copy of the GNU General Public License
  15.  *     along with this program; if not, write to the Free Software
  16.  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */ 
  18.  
  19. #include "mutt.h"
  20. #include "mutt_curses.h"
  21. #include "mx.h"
  22. #include "mailbox.h"
  23.  
  24. #include <unistd.h>
  25. #include <netinet/in.h>
  26. #include <netdb.h>
  27. #include <string.h>
  28. #include <ctype.h>
  29. #include <stdlib.h>
  30.  
  31. /* Minimal support for IMAP 4rev1 */
  32.  
  33. #define IMAP_PORT 143
  34.  
  35. #define SEQLEN 5
  36.  
  37. /* number of entries in the hash table */
  38. #define IMAP_CACHE_LEN 10
  39.  
  40. enum
  41. {
  42.   IMAP_FATAL = 1,
  43.   IMAP_NEW_MAIL,
  44.   IMAP_BYE
  45. };
  46.  
  47. typedef struct
  48. {
  49.   int index;
  50.   char *path;
  51. } IMAP_CACHE;
  52.  
  53. typedef struct
  54. {
  55.   short status;
  56.   unsigned short sequence;
  57.   unsigned short newMailCount;
  58.   short xxx;
  59.   IMAP_CACHE cache[IMAP_CACHE_LEN];
  60. } IMAP_DATA;
  61.  
  62. static char ImapUser[SHORT_STRING] = { 0 };
  63. static char ImapPass[SHORT_STRING] = { 0 };
  64.  
  65. static int imap_read_line (char *buf, size_t buflen, int fd)
  66. {
  67.   char ch;
  68.   int i;
  69.  
  70.   for (i = 0; i < buflen; i++)
  71.   {
  72.     if (read (fd, &ch, 1) != 1)
  73.       return (-1);
  74.     if (ch == '\n')
  75.       break;
  76.     buf[i] = ch;
  77.   }
  78.   buf[i-1] = 0;
  79.   return (i + 1);
  80. }
  81.  
  82. static int imap_read_line_d (char *buf, size_t buflen, int fd)
  83. {
  84.   int r = imap_read_line (buf, buflen, fd);
  85.   dprint (1,(debugfile,"imap_read_line_d():%s\n", buf));
  86.   return r;
  87. }
  88.  
  89. static void imap_make_sequence (char *buf, size_t buflen, CONTEXT *ctx)
  90. {
  91.   snprintf (buf, buflen, "a%04d", ((IMAP_DATA *) ctx->data)->sequence++);
  92. }
  93.  
  94. static int imap_write (int fd, const char *buf)
  95. {
  96.   dprint (1,(debugfile,"imap_write():%s", buf));
  97.   return (write (fd, buf, strlen (buf)));
  98. }
  99.  
  100. static void imap_error (const char *where, const char *msg)
  101. {
  102.   dprint (1, (debugfile, "imap_error(): unexpected response in %s: %s\n", where, msg));
  103. }
  104.  
  105. /* date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */
  106. static time_t imap_parse_date (char *s)
  107. {
  108.   struct tm t;
  109.   time_t tz;
  110.  
  111.   t.tm_mday = (s[0] - '0') * 10 + (s[1] - '0');
  112.   s += 2;
  113.   if (*s != '-')
  114.     return 0;
  115.   s++;
  116.   t.tm_mon = mutt_check_month (s);
  117.   s += 3;
  118.   if (*s != '-')
  119.     return 0;
  120.   s++;
  121.   t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900;
  122.   s += 4;
  123.   if (*s != ' ')
  124.     return 0;
  125.   s++;
  126.  
  127.   /* time */
  128.   t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0');
  129.   s += 2;
  130.   if (*s != ':')
  131.     return 0;
  132.   s++;
  133.   t.tm_min = (s[0] - '0') * 10 + (s[1] - '0');
  134.   s += 2;
  135.   if (*s != ':')
  136.     return 0;
  137.   s++;
  138.   t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0');
  139.   s += 2;
  140.   if (*s != ' ')
  141.     return 0;
  142.   s++;
  143.  
  144.   /* timezone */
  145.   tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 +
  146.     ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
  147.   if (s[0] == '+')
  148.     tz = -tz;
  149.  
  150.   return (mutt_mktime (&t) + tz);
  151. }
  152.  
  153. static int imap_parse_fetch (HEADER *h, char *s)
  154. {
  155.   char tmp[SHORT_STRING];
  156.   char *ptmp;
  157.   int state = 0;
  158.  
  159.   if (!s)
  160.     return (-1);
  161.  
  162.   h->read = 0;
  163.   h->old = 0;
  164.  
  165.   while (*s)
  166.   {
  167.     SKIPWS (s);
  168.  
  169.     switch (state)
  170.     {
  171.       case 0:
  172.     if (strncasecmp ("FLAGS", s, 5) == 0)
  173.     {
  174.       s += 5;
  175.       SKIPWS (s);
  176.       if (*s != '(')
  177.       {
  178.         dprint (1, (debugfile, "imap_parse_fetch(): bogus FLAGS entry: %s\n", s));
  179.         return (-1); /* parse error */
  180.       }
  181.       s++;
  182.       state = 1;
  183.     }
  184.     else if (strncasecmp ("INTERNALDATE", s, 12) == 0)
  185.     {
  186.       s += 12;
  187.       SKIPWS (s);
  188.       if (*s != '\"')
  189.       {
  190.         dprint (1, (debugfile, "imap_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
  191.         return (-1);
  192.       }
  193.       s++;
  194.       ptmp = tmp;
  195.       while (*s && *s != '\"')
  196.         *ptmp++ = *s++;
  197.       if (*s != '\"')
  198.         return (-1);
  199.       s++; /* skip past the trailing " */
  200.       *ptmp = 0;
  201.       h->received = imap_parse_date (tmp);
  202.     }
  203.     else if (strncasecmp ("RFC822.SIZE", s, 11) == 0)
  204.     {
  205.       s += 11;
  206.       SKIPWS (s);
  207.       ptmp = tmp;
  208.       while (isdigit (*s))
  209.         *ptmp++ = *s++;
  210.       *ptmp = 0;
  211.     }
  212.     else if (*s == ')')
  213.       s++; /* end of request */
  214.     else
  215.     {
  216.       /* got something i don't understand */
  217.       imap_error ("imap_parse_fetch()", s);
  218.       return (-1);
  219.     }
  220.     break;
  221.       case 1: /* flags */
  222.     if (*s == ')')
  223.     {
  224.       s++;
  225.       state = 0;
  226.     }
  227.     else if (strncasecmp ("\\deleted", s, 8) == 0)
  228.     {
  229.       s += 8;
  230.       h->deleted = 1;
  231.     }
  232.     else if (strncasecmp ("\\flagged", s, 8) == 0)
  233.     {
  234.       s += 8;
  235.       h->flagged = 1;
  236.     }
  237.     else if (strncasecmp ("\\answered", s, 9) == 0)
  238.     {
  239.       s += 9;
  240.       h->replied = 1;
  241.     }
  242.     else if (strncasecmp ("\\seen", s, 5) == 0)
  243.     {
  244.       s += 5;
  245.       h->read = 1;
  246.     }
  247.     else
  248.     {
  249.       while (*s && !ISSPACE (*s) && *s != ')')
  250.         s++;
  251.     }
  252.     break;
  253.     }
  254.   }
  255.   return 0;
  256. }
  257.  
  258. static int imap_read_bytes (FILE *fp, int fd, long bytes)
  259. {
  260.   long pos;
  261.   long len;
  262.   char buf[LONG_STRING];
  263.  
  264.   for (pos = 0; pos < bytes; )
  265.   {
  266.     len = imap_read_line (buf, sizeof (buf), fd);
  267.     if (len < 0)
  268.       return (-1);
  269.     pos += len;
  270.     fputs (buf, fp);
  271.     fputs ("\n", fp);
  272.   }
  273.  
  274.   return 0;
  275. }
  276.  
  277. /* returns 1 if the command result was OK, or 0 if NO or BAD */
  278. static int imap_code (const char *s)
  279. {
  280.   s += SEQLEN;
  281.   SKIPWS (s);
  282.   return (strncasecmp ("OK", s, 2) == 0);
  283. }
  284.  
  285. static char *imap_next_word (char *s)
  286. {
  287.   while (*s && !ISSPACE (*s))
  288.     s++;
  289.   SKIPWS (s);
  290.   return s;
  291. }
  292.  
  293. static int imap_handle_untagged (CONTEXT *ctx, char *s)
  294. {
  295.   char *pn;
  296.   int count;
  297.  
  298.   s = imap_next_word (s);
  299.  
  300.   if (isdigit (*s))
  301.   {
  302.     pn = s;
  303.     s = imap_next_word (s);
  304.  
  305.     if (strncasecmp ("EXISTS", s, 6) == 0)
  306.     {
  307.       /* new mail arrived */
  308.       count = atoi (pn);
  309.  
  310.       if (count <= ctx->msgcount)
  311.       {
  312.     /* something is wrong because the server reported fewer messages
  313.      * than we previously saw
  314.      */
  315.     mutt_error ("Fatal error.  Message count is out of sync!");
  316.     ((IMAP_DATA *) ctx->data)->status = IMAP_FATAL;
  317.     mx_fastclose_mailbox (ctx);
  318.     return (-1);
  319.       }
  320.       else
  321.       {
  322.     ((IMAP_DATA *) ctx->data)->status = IMAP_NEW_MAIL;
  323.     ((IMAP_DATA *) ctx->data)->newMailCount = count;
  324.       }
  325.     }
  326.   }
  327.   else if (strncasecmp ("BYE", s, 3) == 0)
  328.   {
  329.     /* server shut down our connection */
  330.     s += 3;
  331.     SKIPWS (s);
  332.     mutt_error (s);
  333.     ((IMAP_DATA *) ctx->data)->status = IMAP_BYE;
  334.     mx_fastclose_mailbox (ctx);
  335.     return (-1);
  336.   }
  337.   else
  338.   {
  339.     dprint (1, (debugfile, "imap_unhandle_untagged(): unhandled request: %s\n",
  340.         s));
  341.   }
  342.  
  343.   return 0;
  344. }
  345.  
  346. static int imap_read_header (CONTEXT *ctx, int msgno)
  347. {
  348.   char buf[LONG_STRING];
  349.   FILE *fp;
  350.   char tempfile[_POSIX_PATH_MAX];
  351.   char seq[8];
  352.   char *pc;
  353.   char *pn;
  354.   long bytes;
  355.  
  356.   ctx->hdrs[ctx->msgcount]->index = ctx->msgcount;
  357.  
  358.   mutt_mktemp (tempfile);
  359.   if (!(fp = safe_fopen (tempfile, "w+")))
  360.   {
  361.     return (-1);
  362.   }
  363.  
  364.   imap_make_sequence (seq, sizeof (seq), ctx);
  365.   snprintf (buf, sizeof (buf), "%s FETCH %d RFC822.HEADER\r\n", seq, msgno + 1);
  366.   imap_write (ctx->fd, buf);
  367.  
  368.   do
  369.   {
  370.     if (imap_read_line (buf, sizeof (buf), ctx->fd) < 0)
  371.     {
  372.       return (-1);
  373.     }
  374.  
  375.     if (buf[0] == '*')
  376.     {
  377.       pc = buf;
  378.       pc = imap_next_word (pc);
  379.       pc = imap_next_word (pc);
  380.       if (strncasecmp ("FETCH", pc, 5) == 0)
  381.       {
  382.     if (!(pc = strchr (pc, '{')))
  383.     {
  384.       imap_error ("imap_read_header()", buf);
  385.       return (-1);
  386.     }
  387.     pc++;
  388.     pn = pc;
  389.     while (isdigit (*pc))
  390.       pc++;
  391.     *pc = 0;
  392.     bytes = atoi (pn);
  393.  
  394.     imap_read_bytes (fp, ctx->fd, bytes);
  395.       }
  396.       else if (imap_handle_untagged (ctx, buf) != 0)
  397.       return (-1);
  398.     }
  399.   }
  400.   while (strncmp (seq, buf, SEQLEN) != 0);
  401.  
  402.   rewind (fp);
  403.   ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno]);
  404.  
  405.   fclose (fp);
  406.   unlink (tempfile);
  407.  
  408.   /* get the status of this message */
  409.   imap_make_sequence (seq, sizeof (seq), ctx);
  410.   snprintf (buf, sizeof (buf), "%s FETCH %d FAST\r\n", seq, msgno + 1);
  411.   imap_write (ctx->fd, buf);
  412.   do
  413.   {
  414.     if (imap_read_line_d (buf